﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using LGE;

namespace MagicFight
{
    public class Character : GameObject
    {
        #region Atributtes
        public int TotalHP = 100;
        public int TotalMP = 100;
        public int HP = 100;
        public int MP = 100;
        public float velocity = 5;
        public bool countered = false;
        #endregion

        // movement
        Vector2 moveDirection = Vector2.Zero;

        // regen
        int mpRegen = 1; // per second
        int hpRegen = 1; // per second
        double regenDelay = 0;

        // magias que possui
        public int[] magic = { Def.ReflectDef.N, Def.ArcaneShotDef.N, Def.DevotionDef.N, Def.GodBlessDef.N, Def.RicochetDef.N, 0, 0, 0 };
        public int[] points = { 1, 1, 1, 1 }; // quantidade de pontos de cada MagicType

        // others
        public bool isCasting = false;
        public int castTime = 1;
        public double castTimer = 0;
        public int playerIndex;
        public String name;

        public Character(int playerIndex, String name, Texture2D sprite)
            : base(sprite)
        {
            this.playerIndex = playerIndex;
            this.name = name;
            direction = Direction.Left;
        }

        public override void Update(GameTime gameTime)
        {
            if (HP <= 0) { HP = 0; MP = 0; alive = false; return; }
            UpdateInput(gameTime);
            UpdateRegen(gameTime);
        }

        private void UpdateInput(GameTime gameTime)
        {
            // quit game
            if (Input.Pressed(playerIndex, KeyMap.Start)) { GameScreenManager.Pop(); GameScreenManager.Push(new StartScreen()); return; }

            // movimentação
            Move(Input.LeftStickState(playerIndex));
            if (Input.RightStickState(playerIndex) != Vector2.Zero) direction = Input.RightStickState(playerIndex); //GameScreenManager.MousePos - this.position; // atualiza a direção e, consequentemente, a rotação ao ser desenhado
            direction.Normalize();

            // magias
            for (int i = 0; i < 8; ++i) 
                if (Input.Pressed(playerIndex, Def.ButtonIndex[i]))
                    ps.magics.Add(Def.MagicDefs[magic[i]].Cast(this));
        }

        public void UpdateRegen(GameTime gameTime)
        {
            if (isCasting) return;
            regenDelay += gameTime.ElapsedGameTime.TotalMilliseconds;
            if (regenDelay > 1000)
            {
                regenDelay -= 1000;
                GainHP(hpRegen);
                GainMP(mpRegen);
            }
        }

        public void Move(Vector2 d)
        {
            if (isCasting) return;
            if (d == null || d == Vector2.Zero) return;
            d.Normalize();
            position += d * velocity;
        }

        #region HP/MP/Points
        /// <summary>
        /// Faz o personagem receber uma quantidade de dano. Este dano será alterado de acordo com os buffs existentes no persoangem.
        /// </summary>
        /// <param name="damage">Dano a ser recebido, antes de sofrer alterações dos buffs.</param>
        /// <returns>Dano que o persoangem de fato recebeu, após sofrer alterações dos buffs.</returns>
        public int TakeDamage(int damage, Character attacker)
        {
            // aqui checa se tem buff que diminui ou aumenta o dano
            #region Variáveis Auxiliares
            float retributionAuraMultiplier = 0; // se for maior que 0, tem RetributionAura ativa para no final fazer o cáculo
            bool isManaShieldActive = false; // diz se tem ManaShield ativo para no final fazer o cálculo
            int baseDamage = damage; // todos os buffs são aplicados em cima do dano baso e depois adicionados ao dano final
            #endregion
            foreach (Magic m in ps.magics)
            {
                if (m.isCasting) continue; // se essa magia ainda está sendo castada, então não deve ser considerada
                #region Protection Buffs
                else if (m is ManaShield && m.caster == this) isManaShieldActive = true;
                else if (m is Harden && m.caster == this) damage -= (int)(baseDamage * (m as Harden).multiplier);
                else if (m is FieldProtection && (m as FieldProtection).CollidesWith(this)) damage -= (int)(baseDamage * (m as FieldProtection).multiplier);
                else if (m is RetributionAura && m.caster == this) retributionAuraMultiplier = (m as RetributionAura).multiplier;
                #endregion
                #region Holy Buffs
                else if (m is HolyPower && m.caster == attacker) damage += (int)(baseDamage * (m as HolyPower).multiplier);
                else if (m is GodBless && m.caster == attacker) damage += (int)(baseDamage * (m as GodBless).multiplier);
                else if (m is GodBless && m.caster == this) { damage = 0; baseDamage = 0; } // GodBless zera o dano recebido independete de qualquer outro buff
                #endregion
            }

            if (damage <= 0) return 0;
            #region Retribution Aura Calc
            int retributionDamage = (int)(damage * retributionAuraMultiplier);
            if (retributionDamage > 0) attacker.TakeDamage(retributionDamage, this);
            #endregion
            #region Mana Shield Calc
            if (isManaShieldActive)
            {
                if (damage >= MP)
                {
                    if (MP > 0)
                        ps.effectsManager.Add(new FloatingText(this.position, "-" + MP + " MP", Color.Blue));
                    damage -= MP;
                    MP = 0;
                    isManaShieldActive = false;
                }
                else
                {
                    ps.effectsManager.Add(new FloatingText(this.position, "-" + damage + " MP", Color.Blue));
                    MP -= damage;
                    damage = 0;
                }
            }
            #endregion
            if (damage >= HP) { damage = HP; MP = 0; alive = false; }
            HP -= damage;
            return damage;
        }

        public int GainHP(int amount)
        {
            if (amount <= 0) return 0;
            if (amount + HP > TotalHP) amount = TotalHP - HP;
            HP += amount;
            return amount;
        }

        public int GainMP(int amount)
        {
            if (amount <= 0) return 0;
            if (amount + MP > TotalMP) amount = TotalMP - MP;
            MP += amount;
            return amount;
        }

        public bool ConsumeMana(int manaCost)
        {
            if (manaCost > MP) return false;
            MP -= manaCost;
            return true;
        }

        public bool AddPoints(MagicType type, int x)
        {
            if (points[(int)type] + x < 0) return false;
            if (points[(int)type] + x > 3) x = 3 - points[(int)type];
            points[(int)type] += x;
            return true;
        }
        #endregion

        public bool StartCastTime(int castTime)
        {
            if (isCasting) return false;
            isCasting = true;
            this.castTime = castTime;
            return true;
        }
    }
}
